home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 126-150 / disk_149 / less / src / screen.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  13KB  |  626 lines

  1. /*
  2.  * Routines which deal with the characteristics of the terminal.
  3.  * Uses termcap to be as terminal-independent as possible.
  4.  *
  5.  * {{ Someday this should be rewritten to use curses. }}
  6.  */
  7.  
  8. #include "less.h"
  9. #if XENIX
  10. #include <sys/types.h>
  11. #include <sys/ioctl.h>
  12. #endif
  13.  
  14. #ifndef AMIGA
  15. #if TERMIO
  16. #include <termio.h>
  17. #else
  18. #include <sgtty.h>
  19. #endif
  20. #endif
  21.  
  22. #ifdef TIOCGWINSZ
  23. #include <sys/ioctl.h>
  24. #else
  25. /*
  26.  * For the Unix PC (ATT 7300 & 3B1):
  27.  * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
  28.  * whether to include sys/window.h.  Use SIGWIND from sys/signal.h instead.
  29.  */
  30. #include <signal.h>
  31. #ifdef SIGWIND
  32. #include <sys/window.h>
  33. #endif
  34. #endif
  35.  
  36. /*
  37.  * Strings passed to tputs() to do various terminal functions.
  38.  */
  39. static char
  40.     *sc_pad,        /* Pad string */
  41.     *sc_home,        /* Cursor home */
  42.     *sc_addline,        /* Add line, scroll down following lines */
  43.     *sc_lower_left,        /* Cursor to last line, first column */
  44.     *sc_move,        /* General cursor positioning */
  45.     *sc_clear,        /* Clear screen */
  46.     *sc_eol_clear,        /* Clear to end of line */
  47.     *sc_s_in,        /* Enter standout (highlighted) mode */
  48.     *sc_s_out,        /* Exit standout mode */
  49.     *sc_u_in,        /* Enter underline mode */
  50.     *sc_u_out,        /* Exit underline mode */
  51.     *sc_b_in,        /* Enter bold mode */
  52.     *sc_b_out,        /* Exit bold mode */
  53.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  54.     *sc_backspace,        /* Backspace cursor */
  55.     *sc_init,        /* Startup terminal initialization */
  56.     *sc_deinit;        /* Exit terminal de-intialization */
  57. static int dumb;
  58. static int hard;
  59.  
  60. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  61. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  62. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  63. public int sc_width, sc_height;    /* Height & width of screen */
  64. public int sc_window = -1;    /* window size for forward and backward */
  65. public int bo_width, be_width;    /* Printing width of boldface sequences */
  66. public int ul_width, ue_width;    /* Printing width of underline sequences */
  67. public int so_width, se_width;    /* Printing width of standout sequences */
  68.  
  69. /*
  70.  * These two variables are sometimes defined in,
  71.  * and needed by, the termcap library.
  72.  * It may be necessary on some systems to declare them extern here.
  73.  */
  74. /*extern*/ short ospeed;    /* Terminal output baud rate */
  75. /*extern*/ char PC;        /* Pad character */
  76.  
  77. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  78. extern int know_dumb;        /* Don't complain about a dumb terminal */
  79. extern int back_scroll;
  80. char *tgetstr();
  81. char *tgoto();
  82.  
  83. /*
  84.  * Change terminal to "raw mode", or restore to "normal" mode.
  85.  * "Raw mode" means 
  86.  *    1. An outstanding read will complete on receipt of a single keystroke.
  87.  *    2. Input is not echoed.  
  88.  *    3. On output, \n is mapped to \r\n.
  89.  *    4. \t is NOT expanded into spaces.
  90.  *    5. Signal-causing characters such as ctrl-C (interrupt),
  91.  *       etc. are NOT disabled.
  92.  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  93.  */
  94.     public void
  95. raw_mode(on)
  96.     int on;
  97. {
  98. #ifdef AMIGA
  99.     extern int do_echo;
  100.  
  101.     if (on)
  102.         do_echo = 0;
  103.     else
  104.         do_echo = 1;
  105.         erase_char = 8; /* ^H */
  106.     kill_char = 24; /* ^X */
  107. #else
  108. #if TERMIO
  109.     struct termio s;
  110.     static struct termio save_term;
  111.  
  112.     if (on)
  113.     {
  114.         /*
  115.          * Get terminal modes.
  116.          */
  117.         ioctl(2, TCGETA, &s);
  118.  
  119.         /*
  120.          * Save modes and set certain variables dependent on modes.
  121.          */
  122.         save_term = s;
  123.         ospeed = s.c_cflag & CBAUD;
  124.         erase_char = s.c_cc[VERASE];
  125.         kill_char = s.c_cc[VKILL];
  126.  
  127.         /*
  128.          * Set the modes to the way we want them.
  129.          */
  130.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  131.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  132.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  133.         s.c_cc[VMIN] = 1;
  134.         s.c_cc[VTIME] = 0;
  135.     } else
  136.     {
  137.         /*
  138.          * Restore saved modes.
  139.          */
  140.         s = save_term;
  141.     }
  142.     ioctl(2, TCSETAW, &s);
  143. #else
  144.     struct sgttyb s;
  145.     static struct sgttyb save_term;
  146.  
  147.     if (on)
  148.     {
  149.         /*
  150.          * Get terminal modes.
  151.          */
  152.         ioctl(2, TIOCGETP, &s);
  153.  
  154.         /*
  155.          * Save modes and set certain variables dependent on modes.
  156.          */
  157.         save_term = s;
  158.         ospeed = s.sg_ospeed;
  159.         erase_char = s.sg_erase;
  160.         kill_char = s.sg_kill;
  161.  
  162.         /*
  163.          * Set the modes to the way we want them.
  164.          */
  165.         s.sg_flags |= CBREAK;
  166.         s.sg_flags &= ~(ECHO|XTABS);
  167.     } else
  168.     {
  169.         /*
  170.          * Restore saved modes.
  171.          */
  172.         s = save_term;
  173.     }
  174.     ioctl(2, TIOCSETN, &s);
  175. #endif
  176. #endif
  177. }
  178.  
  179.     static void
  180. cannot(s)
  181.     char *s;
  182. {
  183.     char message[100];
  184.  
  185.     if (know_dumb)
  186.         /* 
  187.          * He knows he has a dumb terminal, so don't tell him. 
  188.          */
  189.         return;
  190.  
  191.     sprintf(message, "WARNING: terminal cannot \"%s\"", s);
  192.     error(message);
  193. }
  194.  
  195. /*
  196.  * Get terminal capabilities via termcap.
  197.  */
  198.     public void
  199. get_term()
  200. {
  201. #ifdef AMIGA
  202.     static char go_to_home[20];
  203.     extern int nrow;
  204.  
  205. /* I didn't want to port termcap for now, but there is a version
  206.  on fish #14 that someone might want to use */
  207.     sc_pad = "";                /* Pad string */
  208.     sc_home = "\x9b1;1H";        /* Cursor home */
  209.     sc_addline = "\x9bL";       /* Add line, scroll down following lines */
  210.     sprintf(go_to_home, "\x9b%d;1H", nrow);
  211.     sc_lower_left = go_to_home;    /* Cursor to last line, first column */
  212.     sc_move = "";               /* General cursor positioning */
  213.     sc_clear = "\f";            /* Clear screen */
  214.     sc_eol_clear = "\x9bK";     /* Clear to end of line */
  215.     sc_s_in = "\x9b0;32;43m";   /* Enter standout (highlighted) mode */
  216.     sc_s_out = "\x9bm";         /* Exit standout mode */
  217.     sc_u_in = "\x9b4m";         /* Enter underline mode */
  218.     sc_u_out = "\x9bm";         /* Exit underline mode */
  219.     sc_b_in = "\x9b1m";         /* Enter bold mode */
  220.     sc_b_out = "\x9b0m";       /* Exit bold mode */
  221.     sc_visual_bell = "\007";    /* Visual bell (flash screen) sequence */
  222.     sc_backspace = "\b";      /* Backspace cursor */
  223.     sc_init = "";               /* Startup terminal initialization */
  224.     sc_deinit = "";             /* Exit terminal de-intialization */
  225.     sc_height = nrow;
  226.     sc_width = 77;
  227.     sc_window = nrow-1;
  228. #else
  229.     char termbuf[2048];
  230.     char *sp;
  231. #ifdef TIOCGWINSZ
  232.     struct winsize w;
  233. #else
  234. #ifdef WIOCGETD
  235.     struct uwdata w;
  236. #endif
  237. #endif
  238.     static char sbuf[1024];
  239.  
  240.     char *getenv();
  241.  
  242.     /*
  243.      * Find out what kind of terminal this is.
  244.      */
  245.     if (tgetent(termbuf, getenv("TERM")) <= 0)
  246.         dumb = 1;
  247.  
  248.     /*
  249.      * Get size of the screen.
  250.      */
  251. #ifdef TIOCGWINSZ
  252.     if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row)
  253.         sc_height = w.ws_row;
  254.     else
  255. #else
  256. #ifdef WIOCGETD
  257.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height)
  258.         sc_height = w.uw_height/w.uw_vs;
  259.     else
  260. #endif
  261. #endif
  262.          sc_height = tgetnum("li");
  263.      if (dumb || sc_height < 0 || tgetflag("hc"))
  264.     {
  265.         /* Oh no, this is a hardcopy terminal. */
  266.         hard = 1;
  267.         sc_height = 24;
  268.     }
  269.     /*
  270.      * This is terrible - the following if "knows" that it is being
  271.      * executed *after* command line and environment options have
  272.      * already been parsed.  Should it be executed in the main program
  273.      * instead?
  274.      */
  275.     if ((sc_window <= 0) || (sc_window >= sc_height))
  276.         sc_window = sc_height-1;
  277.  
  278. #ifdef TIOCGWINSZ
  279.      if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col)
  280.         sc_width = w.ws_col;
  281.     else
  282. #ifdef WIOCGETD
  283.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width)
  284.         sc_width = w.uw_width/w.uw_hs;
  285.     else
  286. #endif
  287. #endif
  288.          sc_width = tgetnum("co");
  289.      if (dumb || sc_width < 0)
  290.           sc_width = 80;
  291.  
  292.     auto_wrap = tgetflag("am");
  293.     ignaw = tgetflag("xn");
  294.  
  295.     /*
  296.      * Assumes termcap variable "sg" is the printing width of
  297.      * the standout sequence, the end standout sequence,
  298.      * the underline sequence, the end underline sequence,
  299.      * the boldface sequence, and the end boldface sequence.
  300.      */
  301.     if ((so_width = tgetnum("sg")) < 0)
  302.         so_width = 0;
  303.     be_width = bo_width = ue_width = ul_width = se_width = so_width;
  304.  
  305.     /*
  306.      * Get various string-valued capabilities.
  307.      */
  308.     sp = sbuf;
  309.  
  310.     sc_pad = (dumb) ? NULL : tgetstr("pc", &sp);
  311.     if (sc_pad != NULL)
  312.         PC = *sc_pad;
  313.  
  314.     sc_init = (dumb) ? NULL : tgetstr("ti", &sp);
  315.     if (sc_init == NULL)
  316.         sc_init = "";
  317.  
  318.     sc_deinit= (dumb) ? NULL : tgetstr("te", &sp);
  319.     if (sc_deinit == NULL)
  320.         sc_deinit = "";
  321.  
  322.     sc_eol_clear = (dumb) ? NULL : tgetstr("ce", &sp);
  323.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  324.     {
  325.         cannot("clear to end of line");
  326.         sc_eol_clear = "";
  327.     }
  328.  
  329.     sc_clear = (dumb) ? NULL : tgetstr("cl", &sp);
  330.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  331.     {
  332.         cannot("clear screen");
  333.         sc_clear = "\n\n";
  334.     }
  335.  
  336.     sc_move = (dumb) ? NULL : tgetstr("cm", &sp);
  337.     if (hard || sc_move == NULL || *sc_move == '\0')
  338.     {
  339.         /*
  340.          * This is not an error here, because we don't 
  341.          * always need sc_move.
  342.          * We need it only if we don't have home or lower-left.
  343.          */
  344.         sc_move = "";
  345.     }
  346.  
  347.     sc_s_in = (dumb) ? NULL : tgetstr("so", &sp);
  348.     if (hard || sc_s_in == NULL)
  349.         sc_s_in = "";
  350.  
  351.     sc_s_out = (dumb) ? NULL : tgetstr("se", &sp);
  352.     if (hard || sc_s_out == NULL)
  353.         sc_s_out = "";
  354.  
  355.     sc_u_in = (dumb) ? NULL : tgetstr("us", &sp);
  356.     if (hard || sc_u_in == NULL)
  357.         sc_u_in = sc_s_in;
  358.  
  359.     sc_u_out = (dumb) ? NULL : tgetstr("ue", &sp);
  360.     if (hard || sc_u_out == NULL)
  361.         sc_u_out = sc_s_out;
  362.  
  363.     sc_b_in = (dumb) ? NULL : tgetstr("md", &sp);
  364.     if (hard || sc_b_in == NULL)
  365.     {
  366.         sc_b_in = sc_s_in;
  367.         sc_b_out = sc_s_out;
  368.     } else
  369.     {
  370.         sc_b_out = (dumb) ? NULL : tgetstr("me", &sp);
  371.         if (hard || sc_b_out == NULL)
  372.             sc_b_out = "";
  373.     }
  374.  
  375.     sc_visual_bell = (dumb) ? NULL : tgetstr("vb", &sp);
  376.     if (hard || sc_visual_bell == NULL)
  377.         sc_visual_bell = "";
  378.  
  379.     sc_home = (dumb) ? NULL : tgetstr("ho", &sp);
  380.     if (hard || sc_home == NULL || *sc_home == '\0')
  381.     {
  382.         if (*sc_move == '\0')
  383.         {
  384.             cannot("home cursor");
  385.             /*
  386.              * This last resort for sc_home is supposed to
  387.              * be an up-arrow suggesting moving to the 
  388.              * top of the "virtual screen". (The one in
  389.              * your imagination as you try to use this on
  390.              * a hard copy terminal.)
  391.              */
  392.             sc_home = "|\b^";        
  393.         } else
  394.         {
  395.             /* 
  396.              * No "home" string,
  397.              * but we can use "move(0,0)".
  398.              */
  399.             strcpy(sp, tgoto(sc_move, 0, 0));
  400.             sc_home = sp;
  401.             sp += strlen(sp) + 1;
  402.         }
  403.     }
  404.  
  405.     sc_lower_left = (dumb) ? NULL : tgetstr("ll", &sp);
  406.     if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
  407.     {
  408.         if (*sc_move == '\0')
  409.         {
  410.             cannot("move cursor to lower left of screen");
  411.             sc_lower_left = "\r";
  412.         } else
  413.         {
  414.             /*
  415.              * No "lower-left" string, 
  416.              * but we can use "move(0,last-line)".
  417.              */
  418.             strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  419.             sc_lower_left = sp;
  420.             sp += strlen(sp) + 1;
  421.         }
  422.     }
  423.  
  424.     /*
  425.      * To add a line at top of screen and scroll the display down,
  426.      * we use "al" (add line) or "sr" (scroll reverse).
  427.      */
  428.     if (dumb)
  429.         sc_addline = NULL;
  430.     else if ((sc_addline = tgetstr("al", &sp)) == NULL || 
  431.          *sc_addline == '\0')
  432.         sc_addline = tgetstr("sr", &sp);
  433.  
  434.     if (hard || sc_addline == NULL || *sc_addline == '\0')
  435.     {
  436.         cannot("scroll backwards");
  437.         sc_addline = "";
  438.         /* Force repaint on any backward movement */
  439.         back_scroll = 0;
  440.     }
  441.  
  442.     if (dumb || tgetflag("bs"))
  443.         sc_backspace = "\b";
  444.     else
  445.     {
  446.         sc_backspace = tgetstr("bc", &sp);
  447.         if (sc_backspace == NULL || *sc_backspace == '\0')
  448.             sc_backspace = "\b";
  449.     }
  450. #endif
  451. }
  452.  
  453.  
  454. /*
  455.  * Below are the functions which perform all the 
  456.  * terminal-specific screen manipulation.
  457.  */
  458.  
  459.  
  460. /*
  461.  * Initialize terminal
  462.  */
  463.     public void
  464. init()
  465. {
  466.     tputs(sc_init, sc_height, putchr);
  467. }
  468.  
  469. /*
  470.  * Deinitialize terminal
  471.  */
  472.     public void
  473. deinit()
  474. {
  475.     tputs(sc_deinit, sc_height, putchr);
  476. }
  477.  
  478. /*
  479.  * Home cursor (move to upper left corner of screen).
  480.  */
  481.     public void
  482. home()
  483. {
  484.     tputs(sc_home, 1, putchr);
  485. }
  486.  
  487. /*
  488.  * Add a blank line (called with cursor at home).
  489.  * Should scroll the display down.
  490.  */
  491.     public void
  492. add_line()
  493. {
  494.     tputs(sc_addline, sc_height, putchr);
  495. }
  496.  
  497. /*
  498.  * Move cursor to lower left corner of screen.
  499.  */
  500.     public void
  501. lower_left()
  502. {
  503.     tputs(sc_lower_left, 1, putchr);
  504. }
  505.  
  506. /*
  507.  * Ring the terminal bell.
  508.  */
  509.     public void
  510. bell()
  511. {
  512.     if (quiet == VERY_QUIET)
  513.         vbell();
  514.     else
  515.         putchr('\7');
  516. }
  517.  
  518. /*
  519.  * Output the "visual bell", if there is one.
  520.  */
  521.     public void
  522. vbell()
  523. {
  524.     if (*sc_visual_bell == '\0')
  525.         return;
  526.     tputs(sc_visual_bell, sc_height, putchr);
  527. }
  528.  
  529. /*
  530.  * Clear the screen.
  531.  */
  532.     public void
  533. clear()
  534. {
  535.     tputs(sc_clear, sc_height, putchr);
  536. }
  537.  
  538. /*
  539.  * Clear from the cursor to the end of the cursor's line.
  540.  * {{ This must not move the cursor. }}
  541.  */
  542.     public void
  543. clear_eol()
  544. {
  545.     tputs(sc_eol_clear, 1, putchr);
  546. }
  547.  
  548. /*
  549.  * Begin "standout" (bold, underline, or whatever).
  550.  */
  551.     public void
  552. so_enter()
  553. {
  554.     tputs(sc_s_in, 1, putchr);
  555. }
  556.  
  557. /*
  558.  * End "standout".
  559.  */
  560.     public void
  561. so_exit()
  562. {
  563.     tputs(sc_s_out, 1, putchr);
  564. }
  565.  
  566. /*
  567.  * Begin "underline" (hopefully real underlining, 
  568.  * otherwise whatever the terminal provides).
  569.  */
  570.     public void
  571. ul_enter()
  572. {
  573.     tputs(sc_u_in, 1, putchr);
  574. }
  575.  
  576. /*
  577.  * End "underline".
  578.  */
  579.     public void
  580. ul_exit()
  581. {
  582.     tputs(sc_u_out, 1, putchr);
  583. }
  584.  
  585. /*
  586.  * Begin "bold"
  587.  */
  588.     public void
  589. bo_enter()
  590. {
  591.     tputs(sc_b_in, 1, putchr);
  592. }
  593.  
  594. /*
  595.  * End "bold".
  596.  */
  597.     public void
  598. bo_exit()
  599. {
  600.     tputs(sc_b_out, 1, putchr);
  601. }
  602.  
  603. /*
  604.  * Erase the character to the left of the cursor 
  605.  * and move the cursor left.
  606.  */
  607.     public void
  608. backspace()
  609. {
  610.     /* 
  611.      * Try to erase the previous character by overstriking with a space.
  612.      */
  613.     tputs(sc_backspace, 1, putchr);
  614.     putchr(' ');
  615.     tputs(sc_backspace, 1, putchr);
  616. }
  617.  
  618. /*
  619.  * Output a plain backspace, without erasing the previous char.
  620.  */
  621.     public void
  622. putbs()
  623. {
  624.     tputs(sc_backspace, 1, putchr);
  625. }
  626.